#include "YGRenderMethodManager.h"
#include "YGFileManager.h"
#include "YGRoot.h"
#include "YGGame.h"
#include "YGSurface.h"
#include "YGSprite.h"
#include "YGDir.h"

#if (YG_OS_TYPE == YG_OS_TYPE_ANDROID)
	#include "Android\YGRootAndroid.h"
#endif

#include <GLES/gl.h>
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h>
//#include "importgl.h"

#define CGLE() {\
	for (GLint error = glGetError(); error; error = glGetError())\
	{\
		Log::Print("OpenGL error (0x%x) at line %i in file %s", error, __LINE__, __FILE__);\
	}\
}

// http://www.grinninglizard.com/kyra/API/glfixed_8h-source.html

inline GLfixed  GlIntToFixed(int x)            { return x << 16; }
inline GLfixed  GlFloatToFixed(double x )      { return (GLfixed)(x * 65536.f + 0.5f); }
inline GLfixed  GlDoubleToFixed(double x )     { return (GLfixed)(x * 65536.0 + 0.5); }
inline double   GlFixedToDouble(GLfixed f)     { return (double)(f >> 16); }
inline float    GlFixedToFloat(GLfixed f)      { return (float)(f >> 16); }
inline int      GlFixedToInt(GLfixed f)        { return f >> 16; }
inline int      GlFixedToIntRound(GLfixed f)   { return ( f + 0x0800 ) >> 16; }
inline int      GlFixedToIntRoundUp(GLfixed f) { return ( f + 0xffff ) >> 16; }


const GLfixed BOX_MIN = 0;
const GLfixed BOX_MAX = GlIntToFixed(100);

using namespace YoghurtGum;


class Ball
{

public:

	Ball()
	{	
		m_Sprite = RenderMethodManager::Get().GetCurrent()->CreateSurface(16, 16);
	}

	~Ball()
	{

	}

	void Reset(float a_X, float a_Y)
	{
		m_X = a_X;
		m_Y = a_Y;
		m_SpeedX = -1.f;
		m_SpeedY = -0.5f;
	}

	void Update(float a_DT)
	{
		m_X += m_SpeedX;
		m_Y += m_SpeedY;
		if (m_X < 0.f) 
		{ 
			m_X = 0.f;
			m_SpeedX *= -1.f;
		}
		if (m_X + 16 > (float)Device::Get().GetWidth())
		{ 
			m_X = (float)Device::Get().GetWidth() - 16;
			m_SpeedX *= -1.f;
		}
		if (m_Y < 0.f) 
		{ 
			m_Y = 0.f;
			m_SpeedY *= -1.f;
		}
		if (m_Y + 16> (float)Device::Get().GetHeight())
		{ 
			m_Y = (float)Device::Get().GetHeight() - 16;
			m_SpeedY *= -1.f;
		}
	}

	void Render()
	{
		m_Sprite->Draw((int)m_X, (int)m_Y);
	}

	float GetSpeedX() { return m_SpeedX; }
	float GetSpeedY() { return m_SpeedY; }

	int GetX() { return (int)m_X; }
	int GetY() { return (int)m_Y; }

	void ReverseX(float a_X) 
	{
		m_X = a_X;
		m_SpeedX *= -1;
	}

private:

	Surface* m_Sprite;
	float m_X, m_Y;
	float m_SpeedX, m_SpeedY;

};


class Paddle
{

public:

	Paddle()
	{
		m_Sprite = RenderMethodManager::Get().GetCurrent()->CreateSurface(128, 16);

		m_Delta = (Device::Get().GetWidth() - 128) / 40;
		m_X = 0;
		m_Y = Device::Get().GetHeight() - 16;
	}
	~Paddle() { }

	void Update(float a_DT)
	{
		if (Keyboard::Get().Pressed(YGK_LEFT)) { m_X -= 1; }
		if (Keyboard::Get().Pressed(YGK_RIGHT)) { m_X += 1; }

		if (m_Delta * m_X < 0) { m_X = 0; }
		if (m_Delta * m_X + 128 > Device::Get().GetWidth()) { m_X -= 1; }
	}

	void Render()
	{
		m_Sprite->Draw(m_Delta * m_X, m_Y);
	}

	void ResetBall(Ball* a_Ball)
	{
		a_Ball->Reset((float)(m_X * m_Delta + 64 - 8), (float)m_Y - 16);
	}

private:

	Surface* m_Sprite;
	int m_Delta;
	int m_X, m_Y;

};

class Block
{

public:

	Block(int a_X, int a_Y)
	{
		int offsetx = 4;
		int offsety = 4;
		int deltax = Device::Get().GetWidth() / 8;
		int deltay = Device::Get().GetWidth() / 8;

		m_X = offsetx + a_X * deltax;
		m_Y = offsety + a_Y * deltay;

		m_Dead = false;
	}

	~Block() { }

	void Update(float a_DT)
	{

	}

	bool ResolveCollision(Ball* a_Ball)
	{
		if (m_Dead) { return false; }

		if (
			a_Ball->GetX() > m_X && a_Ball->GetX() < m_X + 32 &&
			a_Ball->GetY() > m_Y && a_Ball->GetY() < m_Y + 16
		)
		{
			if (a_Ball->GetSpeedX() > 0.f) { a_Ball->ReverseX((float)m_X); }
			else { a_Ball->ReverseX((float)(m_X + 32)); }

			m_Dead = true;

			return true;
		}

		return false;
	}
	
	void Render()
	{
		if (!m_Dead) { s_Sprite->Draw(m_X, m_Y); }
	}

	static Surface* s_Sprite;

private:

	int m_X, m_Y;
	int m_Width, m_Height;
	bool m_Dead;

};

Surface* Block::s_Sprite = NULL;

class TestGame : public Game
{

public: 

	TestGame() : Game()
	{
		m_Color = 0.f;
	}
	~TestGame()
	{

	}

	void Init()
	{
		Log::Print("Game::Init");

		Dir* test_dir = new Dir("D:\\Projects\\YoghurtGum2\\Tests\\Galaxians\\bin");

		RenderMethodManager::Get().GetCurrent()->SetClearColor(Color(1.f, 1.f, 1.f));

		m_TestSprite = RenderMethodManager::Get().GetCurrent()->CreateSprite("Viper_Mark_II.bmp");
		//m_TestSprite = RenderMethodManager::Get().GetCurrent()->CreateSprite("avatar.png");

		m_Paddle = new Paddle();

		Block::s_Sprite = RenderMethodManager::Get().GetCurrent()->CreateSurface(32, 16);
		m_BlockTotal = 4 * 8;
		m_Block = new Block*[m_BlockTotal];
		for (int y = 0; y < 4; y++)
		{
			for (int x = 0; x < 8; x++)
			{
				m_Block[y * 8 + x] = new Block(x, y);
			}
		}

		m_Ball = new Ball();
		m_Paddle->ResetBall(m_Ball);
	}
	void Update()
	{
		m_Color += 0.05f;
		if (m_Color > 1.f) { m_Color = 0.f; }

		if (Keyboard::Get().Pressed(YGK_UP))
		{
			m_Paddle->ResetBall(m_Ball);
		}

		m_Ball->Update(0.f);

		for (int i = 0; i < m_BlockTotal; i++)
		{
			m_Block[i]->ResolveCollision(m_Ball);
		}

		m_Paddle->Update(0.f);
	}
	void Render()
	{
		for (int i = 0; i < m_BlockTotal; i++)
		{
			m_Block[i]->Render();
		}

		m_Ball->Render();

		m_Paddle->Render();

		if (m_TestSprite) { m_TestSprite->Draw(0.f, 0.f, 0.f); }
	}

	float m_Color;

	Sprite* m_TestSprite;

	Paddle* m_Paddle;
	Ball* m_Ball;
	Block** m_Block;
	int m_BlockTotal;

};

Game* Game::Configure()
{
	return new TestGame;
}